package com.uxsino.simo.indicator.retractor;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.Maps;
import com.uxsino.commons.utils.config.ConfigProp;
import com.uxsino.commons.utils.config.PropElement;
import com.uxsino.reactorq.model.INDICATOR_TYPE;
import com.uxsino.simo.indicator.CompoundIndicator;
import com.uxsino.simo.indicator.IIndicatorField;
import com.uxsino.simo.indicator.Indicator;
import com.uxsino.simo.indicator.expression.ExprEvaluator;
import com.uxsino.simo.networkentity.EntityInfo;
import com.uxsino.simo.query.QueryContext;
import com.uxsino.simo.query.QueryTemplate;
import com.uxsino.simo.utils.ConfigLoadingContext;
import com.uxsino.simo.utils.ConfigPropLoader;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public abstract class ListValueRetractor extends AbstractValueRetractor<List<Map<String, Object>>> {
    // private Logger logger = LoggerFactory.getLogger(this.getClass());

    protected Indicator itemIndicator;

    @ConfigProp(name = "filter", subElement = true)
    @JsonProperty("filter")
    @JSONField(name = "filter")
    private String filterExpression = null;

    public class ColumnEntry {
        public String index;

        public IIndicatorField field;

        @ConfigProp(name = "parser")
        public String parserName;

        @ConfigProp(name = "pattern", trim = false)
        public String regexPattern;

        @ConfigProp(name = "index")
        public String regexGroupIndex;

        public IValueRetractor retractor;

        @ConfigProp(name = "source")
        public String source;

        @ConfigProp(name = "sub")
        public String sub;

        @ConfigProp(name = "format")
        public String format;


        public ColumnEntry(String index, IIndicatorField field) {
            this.index = index;
            this.field = field;
        }

        public void createRetractor(PropElement eCol, ConfigLoadingContext lctxt) throws Exception {
            if ("regex".equals(parserName)) {
                retractor = ValueRetractorFactory.createValueRetractor("regex", field.getFieldType().caseIndType());
                if (regexPattern != null)
                    ((RegexValueRetractor) retractor).setPattern(regexPattern);
                return;
            } else if (parserName != null && parserName.length() != 0) {
                lctxt.error(eCol.getSourceLocation(), "parser type: " + parserName + " is not allowed here. ignored.");
                return;
            }
            retractor = ValueRetractorFactory.createValueRetractor("", field.getFieldType().caseIndType());
        }
    };

    protected List<ColumnEntry> columnEntries;

    private static class FieldExpr {
        public String expr;

        public EXPRTYPE exprType;

        @Override
        public String toString() {
            return expr;
        }
    }

    // fieldName, expr
    private Map<String, FieldExpr> exprFields = new LinkedHashMap<>();

    public ListValueRetractor() {
        super(INDICATOR_TYPE.LIST);
        columnEntries = new ArrayList<>();

    }

    public void setItemValueType(Indicator itemIndicator) {
        this.itemIndicator = itemIndicator;

    }

    @Override
    public void addFieldExpr(String fieldName, EXPRTYPE exprType, String expr) {
        FieldExpr f = new FieldExpr();
        f.expr = expr;
        f.exprType = exprType;
        exprFields.put(fieldName, f);
    }

    public ColumnEntry addColumnIndex(PropElement eCol, ConfigLoadingContext lctxt) {
        ConfigPropLoader loader = new ConfigPropLoader(lctxt);

        if (!(itemIndicator instanceof CompoundIndicator)) {
            lctxt.error(eCol.getSourceLocation(), "can not add column to none compound indicator list: {}.",
                itemIndicator == null ? "null" : itemIndicator.name);
            return null;
        }
        String fieldName = eCol.getProp("field");
        IIndicatorField fld = ((CompoundIndicator) itemIndicator).getField(fieldName);
        if (fld == null) {
            lctxt.error(eCol.getSourceLocation(), "error creating column parser, indicator [" + itemIndicator.name
                    + "] dosen't have field  [" + fieldName + "] ");
            return null;
        }
        String columnId = eCol.getProp("col_id");
        ColumnEntry entry = new ColumnEntry(columnId, fld);
        try {
            loader.loadProperties(entry, eCol);
            entry.createRetractor(eCol, lctxt);
        } catch (Exception e) {
            lctxt.error(eCol.getSourceLocation(),
                "error creating column parser " + itemIndicator.name + "[" + columnId + "] " + "", e);
            return null;
        }
        if (entry.retractor != null) {
            try {
                loader.loadProperties(entry.retractor, eCol);
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                lctxt.error(eCol.getSourceLocation(),
                    "error creating column parser " + itemIndicator.name + "[" + columnId + "] " + "", e);
                return null;
            }
            columnEntries.add(entry);
            return entry;
        }
        return null;
    }

    public void clearColumnIndex() {
        columnEntries.clear();
    }

    @Override
    public void loadProp(Indicator ind, PropElement eRetractor, ConfigLoadingContext lctxt)
        throws IllegalArgumentException {
        /*TODO 解析器不限定指标类型,后续需要调整对应规则 if (!(ind instanceof ListIndicator)) {
            lctxt.error(eRetractor.getSourceLocation(), "{} must be ListIndicator type", ind.name);
            throw new IllegalArgumentException("must be ListIndicator type: " + ind.name);
        }*/
        setItemValueType(ind);

        for (PropElement eCol : eRetractor.getElements("column")) {

            addColumnIndex(eCol, lctxt);

        }
        for (PropElement eParamColumn : eRetractor.getElements("parameter_column")) {
            addFieldExpr(eParamColumn.getProp("field"), EXPRTYPE.PARAMETER_NAME, eParamColumn.getProp("param_name"));
        }

        for (PropElement eParamColumn : eRetractor.getElements("formula_column")) {
            addFieldExpr(eParamColumn.getProp("field"), EXPRTYPE.FORMULA, eParamColumn.getProp("formula"));
        }

    }

    @Override
    @SuppressWarnings("unchecked")
    public void postRetract(EntityInfo entity, QueryContext ctxt, QueryTemplate qt, Object indValue) {
        if (indValue == null) {
            return;
        }
        if (StringUtils.isNoneEmpty(filterExpression)) {
            ExprEvaluator evaluator = ctxt.getExprEvaluator(entity);
            evaluator.filterList(filterExpression, (List<Map<String, Object>>) indValue);
        }
        for (Map.Entry<String, FieldExpr> entry : exprFields.entrySet()) {
            String fieldName = entry.getKey();
            if (entry.getValue().exprType == EXPRTYPE.PARAMETER_NAME) {
                Object fieldValue = ctxt.getQueryParameter(entry.getValue().expr);

                ((List<Map<String, Object>>) indValue).forEach(m -> m.put(fieldName, fieldValue));
            } else {
                List<Map<String, Object>> datas = ((List<Map<String, Object>>) indValue);
                for (int i = 0; i < datas.size(); i++) {
                    Map<String, Object> item = Maps.newHashMap(datas.get(i));
                    //添加索引的引用
                    item.put("$index$", i);
                    datas.get(i).put(fieldName, ctxt.getExprEvaluator().evalExpr(item, entry.getValue().expr));
                }
            }
        }
    }

}
